#include "campaignform.h"
#include "ui_campaignform.h"
#include "dtwentyitemdelegate.h"

#include <campaign.h>
#include <xmlserializer.h>

#include <QDebug>
#include <QCoreApplication>
#include <QTimer>
#include <QHeaderView>
#include <QMessageBox>
#include <QFile>
#include <QLineEdit>

CampaignForm::CampaignForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::CampaignForm)
{
    qRegisterMetaType<Campaign*>();

    ui->setupUi(this);

    // form actions
    addAction(ui->actionAdd_Character_Class);
    addAction(ui->actionAdd_Class_Level);
    addAction(ui->actionAdd_Skill);
    addAction(ui->actionAdd_Special_Ability);

    // source model
    m_dtwentyModel = new DTwentyModel(this);

    // view proxy models
    m_levelsModel = new LevelsProxyModel(this);
    m_levelsModel->setSourceModel(m_dtwentyModel);

    m_skillsModel = new SkillsProxyModel(this);
    m_skillsModel->setSourceModel(m_dtwentyModel);

    m_specialsModel = new SpecialsProxyModel(this);
    m_specialsModel->setSourceModel(m_dtwentyModel);

    // charater class combo
    ui->comboBox->setModel(m_dtwentyModel);

    // levels view
    ui->tableViewLevels->setModel(m_levelsModel);
    connect(ui->tableViewLevels->selectionModel(), &QItemSelectionModel::selectionChanged, this, &CampaignForm::onLevelChanged);
    connect(m_skillsModel, &SkillsProxyModel::dataChanged, m_levelsModel, &LevelsProxyModel::revert);
    connect(m_specialsModel, &SkillsProxyModel::dataChanged, m_levelsModel, &LevelsProxyModel::revert);

    // skills view
    ui->listViewSkills->setModel(m_skillsModel);

    // specials view
    ui->listViewSpecials->setModel(m_specialsModel);


    connect(this, &CampaignForm::sigCharacterClassAdded, this, &CampaignForm::onCharacterClassAdded);
}


CampaignForm::~CampaignForm()
{
    delete ui;
}

void CampaignForm::create()
{
    Campaign* campaign = new Campaign(this);
    m_dtwentyModel->setRoot(campaign);

    on_comboBox_currentIndexChanged(0);
}

bool CampaignForm::open(const QString &fileName)
{
    if (fileName.isEmpty())
    {
        QMessageBox::warning(this, tr("File Name is Empty!"), tr("Can't open empty file name!"));
        return false;
    }

    m_fileName = fileName;
    QFile file(m_fileName);
    if (!file.open(QFile::ReadOnly))
    {
        QMessageBox::warning(this, tr("File Open Failed!"), tr("Can't open file!"));
        return false;
    }

    XmlSerializer serializer;
    Campaign* campaign = qobject_cast<Campaign*>(serializer.deserialize(&file));

    if (!campaign)
    {
        QMessageBox::warning(this, tr("Parsing Campaign Failed!"), tr("File is not a Campaign file!"));
        return false;
    }

    campaign->setParent(this);
    m_dtwentyModel->setRoot(campaign);

    onUpdateCharacterClassComboBox();

    on_comboBox_currentIndexChanged(0);
    return true;
}

bool CampaignForm::save(const QString &fileName)
{
    if (!fileName.isEmpty())
    {
        m_fileName = fileName;
    }

    if (m_fileName.isEmpty())
    {
        return false;
    }

    QFile file(m_fileName);
    if (!file.open(QFile::WriteOnly))
    {
        QMessageBox::warning(this, tr("File Save Failed!"), tr("Can't save file!"));
        return false;
    }

    XmlSerializer serializer;
    serializer.serialize(m_dtwentyModel->root(), &file);

    return true;
}

QFileInfo CampaignForm::fileInfo() const
{
    return QFileInfo(m_fileName);
}

void CampaignForm::onLevelChanged()
{
    QModelIndex index = ui->tableViewLevels->currentIndex();
    if (!index.isValid())
    {
        return;
    }

    m_specialsModel->setClassLevel(index.row());
    onUpdateSpecialListView();
}

void CampaignForm::onUpdateCharacterClassComboBox()
{
    QModelIndex comboRoot = m_dtwentyModel->index(0, 0, QModelIndex());
    while (comboRoot.isValid())
    {
        if (comboRoot.data().toString().contains("character", Qt::CaseInsensitive))
        {
            ui->comboBox->setRootModelIndex(comboRoot);
            break;
        }

        comboRoot = comboRoot.sibling(comboRoot.row()+1, comboRoot.column());
    }

    ui->comboBox->setCurrentIndex(0);
}

void CampaignForm::onUpdateSkillListView()
{
    QModelIndex skillIndex = m_skillsModel->index(0, 0, QModelIndex());
    while (skillIndex.isValid())
    {
        if (skillIndex.data().toString().contains("skill", Qt::CaseInsensitive))
        {
//            qDebug() << Q_FUNC_INFO << "Found skill top level index!";
            ui->listViewSkills->setRootIndex(skillIndex);
            return;
        }
        skillIndex = skillIndex.sibling(skillIndex.row()+1, skillIndex.column());
    }

    qDebug() << Q_FUNC_INFO << "FAILED to find skill top level index!";

}

void CampaignForm::onUpdateSpecialListView()
{
    QModelIndex index = m_specialsModel->index(0, 0, QModelIndex());
    while (index.isValid())
    {
        if (index.data().toString().contains("special", Qt::CaseInsensitive))
        {
//            qDebug() << Q_FUNC_INFO << "Found special top level index!";
            ui->listViewSpecials->setRootIndex(index);
            return;
        }
        index = index.sibling(index.row()+1, index.column());
    }

    qDebug() << Q_FUNC_INFO << "FAILED to find special top level index!";
}

void CampaignForm::onCharacterClassAdded()
{
    QModelIndex parent = ui->comboBox->rootModelIndex();
    int rows = m_dtwentyModel->rowCount(parent);

    ui->comboBox->setCurrentIndex(rows-1);

    on_actionAdd_Class_Level_triggered();
}


QModelIndex CampaignForm::sourceIndex(const QModelIndex &index) const
{
    const QAbstractProxyModel* proxyModel = qobject_cast<const QAbstractProxyModel*>(index.model());
    if (!proxyModel)
    {
        return index;
    }

    return sourceIndex(proxyModel->mapToSource(index));
}


void CampaignForm::on_comboBox_currentIndexChanged(int index)
{
    QModelIndex classIndex = ui->comboBox->rootModelIndex().child(index, 0);
    if (!classIndex.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Character Class index is invalid at" << index;
        return;
    }

    QModelIndex containerIndex = classIndex.child(0,0);
    if (!containerIndex.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Character Class container index is invalid";
        return;
    }


    while (containerIndex.isValid())
    {
        QString containerName = containerIndex.data().toString();
        if (containerName.contains("levels", Qt::CaseInsensitive))
        {
            ui->tableViewLevels->setRootIndex(m_levelsModel->mapFromSource(containerIndex));

            m_specialsModel->setClassLevels(containerIndex);
            m_specialsModel->setClassLevel(0);
        }

        if (containerName.contains("skills", Qt::CaseInsensitive))
        {
            m_skillsModel->setClassSkills(containerIndex);
        }

        containerIndex = containerIndex.sibling(containerIndex.row()+1, containerIndex.column());
    }


    ui->tableViewLevels->hideColumn(1);
    ui->tableViewLevels->hideColumn(2);
    ui->tableViewLevels->hideColumn(3);

    ui->tableViewLevels->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);


    onUpdateSkillListView();
    onUpdateSpecialListView();
}


void CampaignForm::on_pushButtonAddSkill_clicked()
{
    on_actionAdd_Skill_triggered();
}

void CampaignForm::on_pushButtonDelSkill_clicked()
{
    QModelIndex skillsParent = ui->listViewSkills->rootIndex();
    if (!skillsParent.isValid())
    {
        return;
    }

    QModelIndex skillIndex = ui->listViewSkills->currentIndex();
    if (!skillIndex.isValid())
    {
        return;
    }

    QAbstractItemModel* model = ui->listViewSkills->model();
    if (!model)
    {
        return;
    }

    model->removeRows(skillIndex.row(), 1, skillsParent);

}

void CampaignForm::on_pushButtonAddSpecial_clicked()
{
    on_actionAdd_Special_Ability_triggered();
}

void CampaignForm::on_pushButtonDelSpecial_clicked()
{
    QModelIndex specialsParent = ui->listViewSpecials->rootIndex();
    if (!specialsParent.isValid())
    {
        return;
    }

    QModelIndex specialIndex = ui->listViewSpecials->currentIndex();
    if (!specialIndex.isValid())
    {
        return;
    }

    QAbstractItemModel* model = ui->listViewSpecials->model();
    if (!model)
    {
        return;
    }

    model->removeRows(specialIndex.row(), 1, specialsParent);
}



void CampaignForm::on_actionAdd_Skill_triggered()
{
    ui->toolBox->setCurrentWidget(ui->pageSkills);

    QModelIndex skillsParent = ui->listViewSkills->rootIndex();
    if (!skillsParent.isValid())
    {
        return;
    }

    QAbstractItemModel* model = ui->listViewSkills->model();
    if (!model)
    {
        return;
    }

    int row = model->rowCount(skillsParent);

    model->insertRows(row, 1, skillsParent);

    QModelIndex sourceParent = sourceIndex(skillsParent);
    if (!sourceParent.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped source parent is invalid";
        return;
    }

    QModelIndex source = sourceParent.child(row, 0);
    if (!source.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Source index at" << row << "is invalid!";
        return;
    }


    QModelIndex index = m_skillsModel->mapFromSource(source); //parent.child(row, 0);
    if (!index.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped proxy index from inserted index is invalid";
        return;
    }

    ui->listViewSkills->edit(index);
    ui->listViewSkills->model()->sort(0);
}

void CampaignForm::on_actionAdd_Special_Ability_triggered()
{
    ui->toolBox->setCurrentWidget(ui->pageSpecials);

    QModelIndex specialsParent = ui->listViewSpecials->rootIndex();
    if (!specialsParent.isValid())
    {
        return;
    }

    QAbstractItemModel* model = ui->listViewSpecials->model();
    if (!model)
    {
        return;
    }

    int row = model->rowCount(specialsParent);

    model->insertRows(row, 1, specialsParent);

    QModelIndex sourceParent = sourceIndex(specialsParent);
    if (!sourceParent.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped source parent is invalid";
        return;
    }

    QModelIndex source = sourceParent.child(row, 0);
    if (!source.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Source index at" << row << "is invalid!";
        return;
    }


    QModelIndex index = m_specialsModel->mapFromSource(source); //parent.child(row, 0);
    if (!index.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Mapped proxy index from inserted index is invalid";
        return;
    }

    ui->listViewSpecials->edit(index);
    ui->listViewSpecials->model()->sort(0);

}

void CampaignForm::on_actionAdd_Character_Class_triggered()
{
    QModelIndex parent = ui->comboBox->rootModelIndex();
    if (!parent.isValid())
    {
        return;
    }


    int row = m_dtwentyModel->rowCount(parent);
    m_dtwentyModel->insertRows(row, 1, parent);

    QModelIndex index = parent.child(row, 0);
    if (!index.isValid())
    {
        return;
    }

    m_dtwentyModel->setData(index, tr("New Character Class"), Qt::EditRole);

    QTimer::singleShot(0, this, SLOT(onCharacterClassAdded()));


}

void CampaignForm::on_actionAdd_Class_Level_triggered()
{
    QModelIndex levelsParent = ui->tableViewLevels->rootIndex();
    if (!levelsParent.isValid())
    {
        return;
    }

    int row = m_levelsModel->rowCount(levelsParent);
    m_levelsModel->insertRows(row, 1, levelsParent);
    QModelIndex levelIndex = m_levelsModel->index(row, 0, levelsParent);
    ui->tableViewLevels->setCurrentIndex(levelIndex);
}


void CampaignForm::on_comboBox_currentTextChanged(const QString &text)
{
    int index = ui->comboBox->currentIndex();
    QModelIndex classIndex = ui->comboBox->rootModelIndex().child(index, 0);

    if (!classIndex.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Current class index is invalid!";
        return;
    }

    m_dtwentyModel->setData(classIndex, text, Qt::EditRole);
}

void CampaignForm::on_pushButtonAddLevel_clicked()
{
    on_actionAdd_Class_Level_triggered();
}

void CampaignForm::on_pushButtonDelLevel_clicked()
{
    QModelIndex parent = ui->tableViewLevels->rootIndex();
    QModelIndex index = ui->tableViewLevels->currentIndex();
    index = index.sibling(index.row(), 0);

    if (!parent.isValid() || !index.isValid())
    {
        qDebug() << Q_FUNC_INFO << "Parent or index is invalid!";
        return;
    }

    m_levelsModel->removeRows(index.row(), 1, parent);
}

void CampaignForm::on_pushButtonAddClass_clicked()
{
    on_actionAdd_Character_Class_triggered();
}

void CampaignForm::on_pushButtonDelClass_clicked()
{
    QModelIndex parent = ui->comboBox->rootModelIndex();
    QModelIndex index = parent.child(ui->comboBox->currentIndex(), 0);

    if (!parent.isValid() || !index.isValid())
    {
        return;
    }

    m_dtwentyModel->removeRows(index.row(), 1, parent);
}
